package com.cheney.mapboxexample.mapbox;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import com.cheney.mapboxexample.R;
import com.cheney.mapboxexample.common.BitmapUtil;
import com.cheney.mapboxexample.common.MyApplication;
import com.cheney.mapboxexample.common.ViewUtil;
import com.google.gson.JsonObject;
import com.mapbox.bindgen.Expected;
import com.mapbox.bindgen.None;
import com.mapbox.bindgen.Value;
import com.mapbox.geojson.Feature;
import com.mapbox.maps.FeatureExtensionValue;
import com.mapbox.maps.LayerPosition;
import com.mapbox.maps.MapboxMap;
import com.mapbox.maps.QueriedFeature;
import com.mapbox.maps.RenderedQueryGeometry;
import com.mapbox.maps.RenderedQueryOptions;
import com.mapbox.maps.ScreenCoordinate;
import com.mapbox.maps.Style;
import com.mapbox.maps.extension.style.expressions.generated.Expression;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 通过symbol layer创建的 聚合layer
 */
public class ClusterLayer {
    private static final String TAG = "ClusterLayer";

    public interface OnClusterLayerClickListener {
        void onClusterClick(List<String> clusterIdList);
    }

    private static final String CLUSTER_SOURCE_ID = "cluster-source-id";
    private static final String LAYER_ID_SINGLE_POINT = "layer-id-single-points";
    private static final String LAYER_ID_CLUSTER_ = "layer-id-cluster-";
    private static final String LAYER_ID_CLUSTER_TEXT = "layer-id-cluster-text";

    private static final String IMAGE_ID_CLUSTER_TEXT_ICON = "image-id-clustered-text-icon";
    private static final String IMAGE_ID_SINGLE_POINT_ICON = "id";
    private static final String POINT_COUNT = "point_count";

    private final Handler mMainLoopHandler = new Handler(Looper.getMainLooper()); // 主线程处理器
    private final MapboxMap mMapboxMap;
    private final Style mStyle;
    private final Context mContext;
    private final List<String> mAnnotationImageIDList = new ArrayList<>();  // 添加到style中的图片id
    private OnClusterLayerClickListener mOnClusterLayerClickListener;

    public ClusterLayer(Context context, MapboxMap mapboxMap, Style mapStyle) {
        mContext = context;
        mMapboxMap = mapboxMap;
        mStyle = mapStyle;
        initLayerIcons();
        initClusterLayers();
    }

    // 将需要使用到的icon图片添加到style中，以便后续使用
    private void initLayerIcons() {
        // 聚类text层 底图
        mStyle.addImage(IMAGE_ID_CLUSTER_TEXT_ICON,
                BitmapUtil.getBitmapFromDrawable(MyApplication.getContext(), R.drawable.icon_circle_blue));
    }

    // 初始化聚类所需的各层layer
    private void initClusterLayers() {
        //添加地图源 type:GeoJson
        {
//            GeoJsonSource geoJsonSource = new GeoJsonSource.Builder(CLUSTER_SOURCE_ID)
//                    .data("")
//                    .cluster(true)
//                    .clusterRadius(30)
//                    .build();
            JSONObject clusterProperties = new JSONObject();
            try {
                clusterProperties.put("first_item_id",
                        new JSONArray(Expression.min(Expression.toNumber(Expression.get("id"))).toJson()));
            } catch (JSONException e) {
                e.printStackTrace();
            }
            Log.d(TAG, clusterProperties.toString());
            JSONObject jsonObject = new JSONObject();
            try {
                jsonObject.put("type","geojson");
                jsonObject.put("data","");      // 初始为空
                jsonObject.put("cluster",true);
                jsonObject.put("clusterRadius",30);
                jsonObject.put("clusterProperties",clusterProperties);
            } catch (JSONException e) {
                e.printStackTrace();
            }
//            Log.d(TAG, jsonObject.toString());
            Expected<String, Value> out = Value.fromJson(jsonObject.toString());
            Expected<String, None> success = mStyle.addStyleSource(CLUSTER_SOURCE_ID,
                    Objects.requireNonNull(out.getValue()));
            Log.d(TAG,"[initClusterLayers]source create: " +
                    (success.isError() ? success.getError() : "success"));
        }

        JSONObject iconLayout = new JSONObject();
        try {
            iconLayout.put("icon-ignore-placement",true);
            iconLayout.put("icon-allow-overlap",true);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        //layer for single data/icon points
        {
            // 构造filter表达式
            Expression.ExpressionBuilder filterExpression = new Expression.ExpressionBuilder("!");
            filterExpression.has(POINT_COUNT);
            // 构造layer
            JSONObject jsonObject = new JSONObject();
            try {
                jsonObject.put("id", LAYER_ID_SINGLE_POINT);
                jsonObject.put("type","symbol");
                jsonObject.put("source", CLUSTER_SOURCE_ID);
                jsonObject.put("icon-image","{" + IMAGE_ID_SINGLE_POINT_ICON + "}");
                jsonObject.put("layout", iconLayout);
                jsonObject.put("filter", new JSONArray(filterExpression.build().toJson()));
            } catch (JSONException e) {
                e.printStackTrace();
            }
            Expected<String, Value> out = Value.fromJson(jsonObject.toString());
            Expected<String, None> success = mStyle.addStyleLayer(Objects.requireNonNull(out.getValue()),
                    new LayerPosition(null, null, null));
            Log.d(TAG,"[initClusterLayers]single icon layer create: " +
                    (success.isError() ? success.getError() : "success"));
        }

        // layers for cluster
        {
            int[] layers = new int[]{150, 20, 1};   // 按照聚合对象中包含的标记物数量区分
            for (int i = 0; i < layers.length; i++) {
                Expression pointCount = Expression.toNumber(Expression.get(POINT_COUNT));
                Expression.ExpressionBuilder expressionBuilder = new Expression.ExpressionBuilder("all");
                int finalI = i;
                expressionBuilder.has(POINT_COUNT);
                if(finalI == 0) {
                    expressionBuilder.gte(expressionBuilder1 -> {
                        expressionBuilder1.addArgument(pointCount);
                        expressionBuilder1.literal(layers[finalI]);
                        return null;
                    });
                } else {
                    expressionBuilder.gte(expressionBuilder12 -> {
                        expressionBuilder12.addArgument(pointCount);
                        expressionBuilder12.literal(layers[finalI]);
                        return null;
                    });
                    expressionBuilder.lt(expressionBuilder13 -> {
                        expressionBuilder13.addArgument(pointCount);
                        expressionBuilder13.literal(layers[finalI - 1]);
                        return null;
                    });
                }

                JSONObject jsonObject = new JSONObject();
                try {
                    jsonObject.put("id", LAYER_ID_CLUSTER_ + i);
                    jsonObject.put("type","symbol");
                    jsonObject.put("source", CLUSTER_SOURCE_ID);
                    jsonObject.put("icon-image","{first_item_id}");
                    jsonObject.put("layout",iconLayout);
                    jsonObject.put("filter",new JSONArray(expressionBuilder.build().toJson()));
                } catch (JSONException e) {
                    e.printStackTrace();
                }
//            Log.d(TAG,jsonObject.toString());
                Expected<String, Value> out = Value.fromJson(jsonObject.toString());
                Expected<String, None> success = mStyle.addStyleLayer(Objects.requireNonNull(out.getValue()),
                        new LayerPosition(null, null, null));
                Log.d(TAG,"[initClusterLayers]clusters' Layer " + i + " create: " +
                        (success.isError() ? success.getError() : "success"));
            }
        }

        // layer for text
        {
            JSONObject layout = new JSONObject();
            try {
                // ["to-string",["get","point_count"]]
                layout.put("icon-image", IMAGE_ID_CLUSTER_TEXT_ICON);
                layout.put("icon-ignore-placement",true);
                layout.put("icon-allow-overlap",true);
                layout.put("text-field", new JSONArray(Expression.toString(Expression.get(POINT_COUNT)).toJson()));
                layout.put("text-size",15.0f);
                layout.put("text-ignore-placement",true);
                layout.put("text-allow-overlap",true);
            } catch (JSONException e) {
                e.printStackTrace();
            }

            JSONObject paint = new JSONObject();
            try {
                paint.put("text-color","#000000");
            } catch (JSONException e) {
                e.printStackTrace();
            }

            JSONObject jsonObject = new JSONObject();
            try {
                jsonObject.put("id", LAYER_ID_CLUSTER_TEXT);
                jsonObject.put("type", "symbol");
                jsonObject.put("source", CLUSTER_SOURCE_ID);
                jsonObject.put("layout", layout);
                jsonObject.put("paint", paint);
                jsonObject.put("filter", new JSONArray(Expression.has(POINT_COUNT).toJson()));
            } catch (JSONException e) {
                e.printStackTrace();
            }
            Expected<String, Value> out = Value.fromJson(jsonObject.toString());
            Expected<String, None> success = mStyle.addStyleLayer(Objects.requireNonNull(out.getValue()),
                    new LayerPosition(null, null, null));
            Log.d(TAG,"[initClusterLayers]text layers create: " +
                    (success.isError() ? success.getError() : "success"));
        }
    }

    // 设置当前标记
    public void setClusterMarkers(List<PhotoAnnotation> photoAnnotationList) {
        // 在线程中执行加载图片的耗时操作
        new Thread(() -> doSetClusterMarkers(photoAnnotationList)).start();
    }

    /**
     * 向地图中设置 标记
     * 有耗时操作,需要在线程中进行
     * @param photoAnnotationList 所有标记数据
     */
    public void doSetClusterMarkers(List<PhotoAnnotation> photoAnnotationList) {
        if (photoAnnotationList == null || photoAnnotationList.size() <= 0) {
            Log.d(TAG,"[setClusterMarkers]return, photoAnnotationList is null");
            return;
        }
        MapGeoJson mapGeoJson = MapGeoJsonUtil.getMapGeoJsonFromList(photoAnnotationList);
        if (mapGeoJson == null) {
            Log.d(TAG,"[setClusterMarkers]return, getMapGeoJsonFromList is null");
            return;
        }
        List<MapGeoJson.FeaturesBean> markFeatures = mapGeoJson.getFeatures();
        if (markFeatures == null || markFeatures.size() <= 0) {
            Log.d(TAG,"[setClusterMarkers]return, mapGeoJson.getFeatures() is null");
            return;
        }
        int width = MyApplication.getScreenWidthPixels();// 屏幕宽度（像素）
        int height = MyApplication.getScreenHeightPixels();// 屏幕高度（像素）
        // 添加显示图片到style中
        for (MapGeoJson.FeaturesBean featuresBean : markFeatures) {
            MapGeoJson.FeaturesBean.FeatureProperties properties = featuresBean.getProperties();
            addImageIntoStyle(properties.getId(), properties.getImageName(), width, height);
        }
        // 切回主线程设置UI
        mMainLoopHandler.post(() -> setClusterSourceProperties(mapGeoJson));
    }

    private void addImageIntoStyle(String id, String imageName, int layoutWidth, int layoutHeight) {
        // 图片已经插入 跳过
        if (mStyle.getStyleImage(id) != null) {
            return;
        }
        View view = View.inflate(MyApplication.getContext(), R.layout.item_map_image_view, null);
        ImageView imageView = view.findViewById(R.id.item_map_image);
        imageView.setImageBitmap(BitmapUtil.getImageFromAssetsFile(mContext, imageName));
        ViewUtil.layoutView(view, layoutWidth, layoutHeight);
        Bitmap markImage = BitmapUtil.getBitmapFromView(view);
        mStyle.addImage(id, markImage);
        mAnnotationImageIDList.add(id);
    }

    /**
     * 设置cluster点击监听
     */
    public void setOnClusterClickListener(OnClusterLayerClickListener listener) {
        mOnClusterLayerClickListener = listener;
    }

    /**
     * 清除style中marker的图像缓存
     */
    public void clearMarkersImageInStyle() {
        if (mStyle == null || mAnnotationImageIDList.isEmpty()) {
            return;
        }
        for (String imageId : mAnnotationImageIDList) {
            mStyle.removeStyleImage(imageId);
        }
        mAnnotationImageIDList.clear();
    }

    /**
     * 向聚类源中设置数据集
     * @param mapGeoJson 为空时则清空数据
     */
    private void setClusterSourceProperties(MapGeoJson mapGeoJson) {
        if (mStyle == null) {
            return;
        }
        JSONObject properties = new JSONObject();
        try {
            properties.put("type","geojson");
            if (mapGeoJson == null) {
                properties.put("data","");
            }else {
                properties.put("data",mapGeoJson.crateJson());
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        Expected<String, Value> out = Value.fromJson(properties.toString());
        if (out.getValue() == null) {
            Log.d(TAG,"[setClusterSourceProperties]property turn to value error:" + out.getError());
            return;
        }
        Expected<String, None> success = mStyle.setStyleSourceProperties(CLUSTER_SOURCE_ID, out.getValue());
        Log.d(TAG,"[setClusterSourceProperties]" + (success.isError() ? success.getError() : "success"));
    }

    /**
     * 地图点击事件响应（用于判断被点击组件是否为cluster）
     * @param clickX 触摸点相对于mapView左上角的坐标X
     * @param clickY 触摸点相对于mapView左上角的坐标Y
     */
    public void onMapClickEvent(float clickX, float clickY) {
        if (mMapboxMap == null || mOnClusterLayerClickListener == null) {
            return;
        }
//        Log.d(TAG,"onMapClickEvent(" + clickX + "," + clickY + ")");
        // 单张图片点击事件
        mMapboxMap.queryRenderedFeatures(
                new RenderedQueryGeometry(new ScreenCoordinate(clickX, clickY)),
                new RenderedQueryOptions(Collections.singletonList(LAYER_ID_SINGLE_POINT), null),
                features -> {
                    if (features.getError() != null || features.getValue() == null) {
                        return;
                    }
                    List<QueriedFeature> list = features.getValue();
                    if (list == null || list.size() <= 0) {
                        return;
                    }
                    // 默认取第一个
                    QueriedFeature curFeature = list.get(0);
                    JsonObject jsonObject = curFeature.getFeature().properties();
                    if (jsonObject == null) {
                        return;
                    }
                    Log.d(TAG,"[getClusterChildFromClick]"+jsonObject.toString());
                    String id = jsonObject.get("id").getAsString();
                    mOnClusterLayerClickListener.onClusterClick(Collections.singletonList(id));
                });

        // 聚合点点击事件
        mMapboxMap.queryRenderedFeatures(
                new RenderedQueryGeometry(new ScreenCoordinate(clickX, clickY)),
                new RenderedQueryOptions(Arrays.asList(LAYER_ID_CLUSTER_ + "0",
                        LAYER_ID_CLUSTER_ + "1",
                        LAYER_ID_CLUSTER_ + "2"), null),
                features -> {
                    if (features.getError() != null) {
                        return;
                    }
                    List<QueriedFeature> list = features.getValue();
                    if (list == null || list.size() <= 0) {
                        return;
                    }
                    // 默认取第一个
                    QueriedFeature curFeature = list.get(0);
                    Log.d(TAG,"[getClusterChildFromClick]"+curFeature.toString());
                    // 查询该聚合下的子集
                    long maxPointNum = 5000;    // 单次查询返回子集数量最大值
                    mMapboxMap.getGeoJsonClusterLeaves(CLUSTER_SOURCE_ID, curFeature.getFeature(),
                            maxPointNum, extension -> {
                                if (extension.getError() != null || extension.getValue() == null) {
                                    return;
                                }
                                FeatureExtensionValue features1 = extension.getValue();
                                if (features1 == null || features1.getFeatureCollection() == null) {
                                    return;
                                }
//                                        Log.d(TAG,"include features size:"+features.getFeatureCollection().size());
                                List<String> idList = new ArrayList<>();
                                for (Feature feature : features1.getFeatureCollection()) {
                                    String id = feature.getProperty("id").getAsString();
                                    idList.add(id);
                                }
                                mOnClusterLayerClickListener.onClusterClick(idList);
                            });
                });
    }

}
